package controlLayer;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import utilLayer.AlreadyExistsException;
import dbLayer.DBBlock;
import dbLayer.DBClass;
import dbLayer.DBConnection;
import dbLayer.DBSchedule;
import dbLayer.DBScheduleIF;
import modelLayer.Block;
import modelLayer.Schedule;

/**
 * This class is a control layer class of the MSAS system. It functions as the link between 
 * our database and the interface. This class is limited to working with the Schedule class,
 * and whichever other classes schedule is associated with. 
 * 
 * @author Danny M�lgaard, Anna Kelemen, Jakob Korsgaard
 * 		   
 * @version 1.3.1
 * 					1.3.1: 		Added more extensive documentation. Verified that
 * 								information contained in the Design Class Diagram,
 * 								is in accordance with the information contained in this
 * 								class.
 *					1.3.2:		Try&catch and throws Exception added to the constructor in order to catch
 *								a NullPointerException and to avoid the program from crashing
 *								if there is no connection to the database. 
 *								updateSchedule() modified
 *								moveTeachingBlock() commented
 *								fields 'suggestion' and 'schedule' removed
 */
public class ScheduleCtr {
	
	private DBScheduleIF dbSchedule;
	
	
	/**
	 * Constructs an instance of ScheduleCtr. Initializes an instance of DBSchedule.
	 * @throws Exception 
	 */
	public ScheduleCtr() throws Exception {
		try {
			dbSchedule = new DBSchedule();
		}
		catch (Exception e){
			System.out.println("ScheduleCtr DBClass init: " + e);
			throw new Exception ("DBConnection initialization error.");
		}
		
	}
	
	/**
	 * Sends information from the interface on to the database layer, more specifically
	 * helping in the insertion of a new row in the database. Begins a transactions upon
	 * calling the dbLayer method, and rolls it back should anything that could result in dataloss,
	 * or unintended data manipulation occur.
	 * 
	 * @param	clName			String	the name of the class the schedule
	 * 									is associated with.
	 * @param	numberOfBlocks	int		number of blocks to add.
	 * @return	rc				int		the count of rows changed in the database
	 * @throws 					SQLException 
	 */
	public int createSchedule(String clName, int numberOfBlocks) throws Exception {
		
		int rc = -1;
		try {
			DBConnection.startTransaction();
			if (new DBClass().find(clName, false) != null) {
				rc = this.dbSchedule.insert(clName, numberOfBlocks);
			}
			DBConnection.commitTransaction();
		}
		catch (SQLException ex) {
			DBConnection.rollbackTransaction();
			throw new AlreadyExistsException("Et skema for " + clName );
		}
		catch (Exception e) {
			DBConnection.rollbackTransaction();
			System.out.println("DEBUG MESSAGE [SCHEDULECTR]: createSchedule(): " + e);
		}
		
		return rc;
		
	}
	
	/**
	 * Sends information from the interface on to the database layer, more specifically
	 * helping in the search of an existing row in the database.
	 * 
	 * @param	clName				String			the name of the class the schedule
	 * 												is associated with.
	 * @param	retrieveAssociation	boolean			determines if we're to retrieve 
	 * 												associations or not.
	 * @return						Schedule		returns the schedule matching the search.
	 */
	public Schedule findScheduleByName(String clName, boolean retrieveAssociation) {

		Schedule sch = null;
		try {
			sch = this.dbSchedule.find(clName, retrieveAssociation);
		}
		catch (Exception e) {
			System.out.println("DEBUG MESSAGE [SCHEDULECTR]: findScheduleByName(): " + e);
		}

		return sch;

	}
	
	/**
	 * Sends information from the interface on to the database layer, more specifically
	 * helping in the search of an existing row in the database.
	 * 
	 * @param	id					int				id of the schedule.
	 * @param	retrieveAssociation	boolean			determines if we're to retrieve 
	 * 												associations or not.
	 * @return						Schedule		returns the schedule matching the search.
	 */
	public Schedule findScheduleById(int id, boolean retrieveAssociation) {
		
		Schedule sch = null;
		try {
			sch = this.dbSchedule.findById(id, retrieveAssociation);
		}
		catch (Exception e) {
			System.out.println("DEBUG MESSAGE [SCHEDULECTR]: findScheduleById(): " + e);
		}
		
		return sch;
		
	}
		
	/**
	 * Sends information from the interface on to the database layer, more specifically
	 * helping in the search of all existing rows in the database.
	 * 
	 * @param	retrieveAssociation	boolean			determines if we're to retrieve 
	 * 												associations or not.
	 * @return	all					List<Schedule>  returns a list of Schedules.
	 */
	public List<Schedule> getAll(boolean retrieveAssociation) {

		List<Schedule> all = new ArrayList<>();
		try {
			all = this.dbSchedule.getAll(retrieveAssociation);
//			if (all.size() == 0) {
//				all = null;
//			}
		}
		catch (Exception e){
			System.out.println("DEBUG MESSAGE [SCHEDULECTR]: getAll(): " + e);
		}

		return all;

	}
	
	/**
	 * Sends information from the interface on to the database layer, more specifically
	 * helping in the renewal of one or more existing rows in the database. Begins a transactions upon
	 * calling the dbLayer method, and rolls it back should anything that could result in data loss,
	 * or unintended data manipulation occur.
	 * 
	 * This method calls updateBlock(...) from the database layer, and calls it for every block in the
	 * schedule.
	 * 
	 * @throws				Exception
	 * @return	rc			int			the count of rows changed in the database
	 */
	public int updateSchedule(Schedule sch, List<Block> blocks) throws Exception{

		int rc = -1;

		try {
			DBConnection.startTransaction();
			for (Block b : blocks){
				rc += new DBBlock().update(sch.getId(), b.getBlockNumber(), b.getTeachingBlock()); 
			}
			DBConnection.commitTransaction();
		}
		catch (Exception e) {
			DBConnection.rollbackTransaction();
			System.out.println("DEBUG MESSAGE [SCHEDULECTR]: updateSchedule(): " + e);
		}
		
		return rc;
		
	}
	
	/**
	 * Sends information from the interface on to the database layer, more specifically
	 * helping in the removal of an existing row in the database. Begins a transactions upon
	 * calling the dbLayer method, and rolls it back should anything that could result in data loss,
	 * or unintended data manipulation occur.
	 * 
	 * @param	sch			Schedule	an instance of class Schedule
	 * @return	rc			int			the count of rows changed in the database
	 */
	public int removeSchedule(Schedule sch) {
		
		int rc = -1;
		String clName = sch.getCl().getClassName();

		try {
			DBConnection.startTransaction();
			rc = this.dbSchedule.remove(clName);
			DBConnection.commitTransaction();
		}
		catch (Exception e) {
			DBConnection.rollbackTransaction();
			System.out.println("DEBUG MESSAGE [SCHEDULECTR]: removeSchedule(): " + e);
		}
		
		return rc;
		
	}
	
//	/**
//	 * This method is the first step in validating the association of a teachingblock with a block.
//	 * It instantiates Validator, in which the conflict check takes place. This method does the
//	 * following: 
//	 * 
//	 * Instantiates class Schedule, a TeachingBlock, a Block and the Validator.
//	 * Calls Validator's checkConflict(...) method, returning a boolean value stating if a
//	 * conflict arose or not. If there's a conflict, it will request a list of integer values, 
//	 * representing available blocks to associate the TeachingBlock with, not including the one with 
//	 * the conflict. It assigns this list to a class variable, making it possible for the interface to get it. 
//	 * If there's no conflict, it will assign the TeachingBlock to the Block, and add the Block
//	 * to the schedule's block collection.
//	 * 
//	 * @param 	clName			String		the class name the schedule is associated with
//	 * @param 	tbId			String		the teachingblock which is to be associated with the schedule block
//	 * @param 	blockNumber		int			the block number in the schedule
//	 * @return	added			boolean		determines if the teachingblock has been added to the block. 
//	 * @throws 	Exception
//	 */
//	public boolean moveTeachingBlock(String clName, int tbId, int blockNumber) throws Exception {
//	
//		boolean added = false;
//
//		try {
//			Schedule sch = this.dbSchedule.find(clName, true); 
//			this.setSchedule(sch);
//			TeachingBlock tb = new TeachingBlockCtr().findTeachingBlockById(tbId, true);
//			Validator v = new Validator();
//			Block b = new DBBlock().find(sch.getId(), blockNumber, true);
//
//			if (v.checkConflict(blockNumber, tb)){
//				this.setSuggestions(v.showSuggestions(this.getSchedule(), b.getBlockNumber(), tb));
//			}
//			else {
//				b.setTeachingBlock(tb);
//				List<Block> blocks = new ArrayList<>();
//				added = blocks.add(b);
//				this.sch.setBlocks(blocks);
//			}
//		}
//		catch (Exception e) {
//			System.out.println("DEBUG MESSAGE [SCHEDULECTR]: moveTeachingBlock(): " + e);
//		}
//
//		return added;
//
//	}

//	public int updateBlock(Schedule sch, TeachingBlock tb, Block b) {
//		int rc = -1;
//		try {
//			DBConnection.startTransaction();
//			rc = new DBBlock().update(sch.getId(), b.getBlockNumber(), tb);
//			DBConnection.commitTransaction();
//		}
//		catch (Exception e){
//			DBConnection.rollbackTransaction();
//			System.out.println("DEBUG MESSAGE [SCHEDULECTR]: updateBlock(): " + e);
//		}
//		
//		return rc;
//	}
	
}
